Skip to content

#574 follow-up: harden guard tests and the temporal resolve swaps its history (PRD-755) - #588

Merged
jonit-dev merged 10 commits into
developfrom
pr574-harden-followup
Oct 29, 2026
Merged

jonit-dev merged 10 commits into
developfrom
pr574-harden-followup

Conversation

@jonit-dev

Copy link
Copy Markdown
Contributor

What is this PR about?

It carries the #574 follow-up work that landed on the lanes after #574 was squash-merged:

  • Harden lane: guard tests that lock gains in place. The camera cull builds no engine Frustum/Ray/Sphere in a real cull frame. A bridge crossing reads the heap buffer once. A physics step counts its bodies, awake dynamics and own microseconds.
  • PRD-755 Phases 1-2: the temporal resolve swaps its history instead of copying it (traa.cpp, with a renderer test). The identity output pass was already skipped. Iris Xe A/B on Hop Garden: no measurable gain (18.91 vs 19.07 ms, balanced pairs).
  • The PRD-756 file, which the same planning commit created (Phase 1 sized and Deferred).

Why does it matter?

The lanes were cut from the pre-squash #574 history. This branch replays the 10 commits on origin/develop, so they can merge.

Done

  • 10 commits cherry-picked onto origin/develop with no conflicts
  • Laptop: build, typecheck, lint green; the three changed specs 36/36

Still open

  • Native build and renderer_test for the traa.cpp change
  • ci:local --affected --base origin/develop --target develop
  • PRD-755 Phase 3

…counted

The shooter's physics bucket grew 2.1 -> 3.4/6.6 ms a frame across two rounds on a
loaded host, inside one unnamed Rapier function, and the trace cannot say whether
more substeps per slow frame or more awake bodies did it. The loop already reports
substeps per window; these are the two missing readings, per step, in the bag a
playtest reads as TN_FRAME_BUDGET.counters.engine:

  physicsBodies / physicsColliders   what the world holds (counts)
  physicsDynamicBodies               dynamic bodies that could be solved (count)
  physicsAwakeBodies                 dynamic bodies Rapier will solve (count, read
                                     before world.step moves the sleep flags)
  physicsStepUs                      the step's own wall clock (microseconds)

Opt-in behind core's ENGINE_COST_KNOB: reading a sleep flag is a Wasm call per
dynamic body, so a shipped frame pays nothing. No rendered pixel changes.

(cherry picked from commit 5cf55b577b60a754131407dd67c4e7a9d9e98416)
(cherry picked from commit fbf27985a7b24dbda03e5648efd0168cf674efd2)
… per use

The bridge-glue lever (9cbfd81, 466 -> 340 ns a crossing) kept no guard a
regression would fail: all 8 per-crossing glue tests passed with the lever
undone. Its win is work per crossing, not a crossing count, so this counts it:
one heap-buffer read per crossing (the engine's own answer) and no HEAPU8.fill
for a call's 64-byte scratch.

Red (9cbfd81's source reverted in the working tree, never committed):
bridge-crossings.spec "reads the heap's buffer once a crossing" --
AssertionError: expected 3 to be less than or equal to 1.
Green (lever restored): 9 passed, that test included.

No rendered pixel changes.

(cherry picked from commit 43ed6a6df5df212f4236df39d92d96e94e289f1c)
The lever (87bfb74, cull 0.78 -> 0.66 ms on the laptop) removed three's
Frustum, Ray and Sphere from the camera pass, which on the web engine are one
boundary call per test each, 70 a frame on the shooter. Its existing guard, the
plain-math equivalence test, only reds because the plain functions disappear with
the undo: it would pass again if the math were right while an engine object came
back to hold it. This counts the constructions in the cull's source instead.
Comments are stripped first, because they name the types the plain code replaces.

Red (87bfb74's source reverted in the working tree, never committed):
render-camera-cull.spec "constructs no three Frustum, Ray or Sphere" --
AssertionError: new Frustum(: expected 'import { type Camera, Frustum, Matrix...'
not to match /new Frustum\(/u.
Green (lever restored): 24 passed, that test included.

No rendered pixel changes.

(cherry picked from commit e988829bd5ff4c8009cfcfe7b63608a3d903fae0)
…e in a real cull frame

e988829bd's guard read the cull's source text, so it passed if an engine Frustum
came back through an alias, a helper or another module. It is now a behaviour
count: the spec's existing `three` mock returns subclasses of Frustum, Ray and
Sphere that count constructions, and one real cull frame on a small scene --
the cull's own construction included, since the lever removed fields built once
per cull -- must add none. The frame asserts it reached every plain-math path
first (near-band rig, band-test rig, off-camera caster, culled prop), so the
zero cannot be vacuous.

Red (66e637840's src reverted in the working tree, never committed):
render-camera-cull.spec "constructs no three Frustum, Ray or Sphere in a real
cull frame" -- AssertionError: expected { Frustum: 1, Ray: 1, Sphere: 1 } to
deeply equal { Frustum: +0, Ray: +0, Sphere: +0 }.
Green (lever restored): 24 passed, that test included.

packages/core 4282 passed | 4 skipped; pnpm typecheck and pnpm lint pass.
No rendered pixel changes.

(cherry picked from commit d1e98d97e7e6962d9424d008a3f3b340d9a913f3)
…istory copies, post blits, shared chain, half-size history)

(cherry picked from commit b219860c33b233bbed64b46aca2570d0699d49ea)
…ng it (PRD-755)

The resolve copied the frame it had just resolved into its history every frame: the display-size
RGBA16Float colour, the RG8 motion mask and the input-size depth, each a read plus a write. It now
reads the history from one slot, writes the next frame's history as a third colour attachment
beside the resolved colour, and the two slots swap, so neither the colour nor the mask is copied.
An attachment write is 8 bytes a pixel where a copy read and wrote 8; copying the caller's own
target instead would move twice the bytes, which is why the resolve takes the attachment in both
cases.

The scene depth stays a copy: it is the caller's texture and the resolve only samples it, so
swapping it would need the renderer to alternate its depth target. `copiesLastFrame()` reports the
three counts, one a steady frame (the depth), zero for the colour and the mask.

red:   native_engine_traa_history_swap  TN_TRAA_HISTORY_COPIES steady=3 (colour 1, depth 1, mask 1)
green: native_engine_traa_history_swap  TN_TRAA_HISTORY_COPIES steady=1 (colour 0, depth 1, mask 0),
       into a caller's target=1 (colour 0, depth 1, mask 0)
bit-identical: native_engine_traa_history_swap_is_bit_identical  120 frames compared, 0 differing
       (the same dumps with the swap undone: 118 of 120 differ)
native_engine_traa_* and native_engine_renderer_* ctests: 95/95 pass, every metric line unchanged
against the run before this change. Estimated 25 MB a frame less traffic at 1080p.

Can change what reaches the screen: no, the resolved frame is bit-identical; the lead runs the
visual judge.

(cherry picked from commit aecafb9bae39f67db5376ab867092004805ade3f)
…afb9ba)

(cherry picked from commit 36939744823efc8650a4ed9eb2b26d5b63c5e02f)
…dy skipped (a0ecc6f, 226ec25)

(cherry picked from commit 28b09a11d7e4122a2dde0bc6f9997b62d6a836be)
…st chain removes 0.44 ms

(cherry picked from commit 4f9b47176d167855173042a33019e2f35a9571da)
…91 vs 19.07 ms, balanced pairs)

(cherry picked from commit 2e1bea11a53df3da6e338606704206f942f904f4)
@jonit-dev jonit-dev added the prd:50% PRD half the phases verified label Oct 28, 2026
@jonit-dev
jonit-dev merged commit 9f4bf90 into develop Oct 29, 2026
49 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

prd:50% PRD half the phases verified

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant